/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.bpm.engine.impl.bpmn.parser.factory;

import com.je.bpm.core.model.ActivitiListener;
import com.je.bpm.core.model.EventListener;
import com.je.bpm.core.model.ImplementationType;
import com.je.bpm.core.model.task.Task;
import com.je.bpm.engine.ActivitiIllegalArgumentException;
import com.je.bpm.engine.delegate.CustomPropertiesResolver;
import com.je.bpm.engine.delegate.ExecutionListener;
import com.je.bpm.engine.delegate.TaskListener;
import com.je.bpm.engine.delegate.TransactionDependentTaskListener;
import com.je.bpm.engine.delegate.event.ActivitiEventListener;
import com.je.bpm.engine.impl.bpmn.helper.*;
import com.je.bpm.engine.impl.bpmn.listener.*;
import com.je.bpm.engine.repository.ProcessDefinition;
import com.je.bpm.engine.runtime.Execution;
import com.je.bpm.engine.runtime.Job;
import com.je.bpm.engine.runtime.ProcessInstance;
import com.je.bpm.engine.task.Attachment;
import com.je.bpm.engine.task.Comment;
import com.je.bpm.engine.task.IdentityLink;

import java.util.HashMap;
import java.util.Map;

/**
 * Default implementation of the {@link ListenerFactory}. Used when no custom {@link ListenerFactory} is injected on the {@link com.je.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl}.
 */
public class DefaultListenerFactory extends AbstractBehaviorFactory implements ListenerFactory {

    private final ClassDelegateFactory classDelegateFactory;

    public DefaultListenerFactory(ClassDelegateFactory classDelegateFactory) {
        this.classDelegateFactory = classDelegateFactory;
    }

    public DefaultListenerFactory() {
        this(new DefaultClassDelegateFactory());
    }

    public static final Map<String, Class<?>> ENTITY_MAPPING = new HashMap<String, Class<?>>();

    static {
        ENTITY_MAPPING.put("attachment", Attachment.class);
        ENTITY_MAPPING.put("comment", Comment.class);
        ENTITY_MAPPING.put("execution", Execution.class);
        ENTITY_MAPPING.put("identity-link", IdentityLink.class);
        ENTITY_MAPPING.put("job", Job.class);
        ENTITY_MAPPING.put("process-definition", ProcessDefinition.class);
        ENTITY_MAPPING.put("process-instance", ProcessInstance.class);
        ENTITY_MAPPING.put("task", Task.class);
    }

    @Override
    public TaskListener createClassDelegateTaskListener(ActivitiListener activitiListener) {
        return classDelegateFactory.create(activitiListener.getImplementation(),
                createFieldDeclarations(activitiListener.getFieldExtensions()));
    }

    @Override
    public TaskListener createExpressionTaskListener(ActivitiListener activitiListener) {
        return new ExpressionTaskListener(expressionManager.createExpression(activitiListener.getImplementation()));
    }

    @Override
    public TaskListener createDelegateExpressionTaskListener(ActivitiListener activitiListener) {
        return new DelegateExpressionTaskListener(expressionManager.createExpression(activitiListener.getImplementation()), createFieldDeclarations(activitiListener.getFieldExtensions()));
    }

    @Override
    public TransactionDependentTaskListener createTransactionDependentDelegateExpressionTaskListener(ActivitiListener activitiListener) {
        return new DelegateExpressionTransactionDependentTaskListener(expressionManager.createExpression(activitiListener.getImplementation()));
    }

    @Override
    public ExecutionListener createClassDelegateExecutionListener(ActivitiListener activitiListener) {
        return classDelegateFactory.create(activitiListener.getImplementation(), createFieldDeclarations(activitiListener.getFieldExtensions()));
    }

    @Override
    public ExecutionListener createExpressionExecutionListener(ActivitiListener activitiListener) {
        return new ExpressionExecutionListener(expressionManager.createExpression(activitiListener.getImplementation()));
    }

    @Override
    public ExecutionListener createDelegateExpressionExecutionListener(ActivitiListener activitiListener) {
        return new DelegateExpressionExecutionListener(expressionManager.createExpression(activitiListener.getImplementation()), createFieldDeclarations(activitiListener.getFieldExtensions()));
    }

    @Override
    public DelegateExpressionTransactionDependentExecutionListener createTransactionDependentDelegateExpressionExecutionListener(ActivitiListener activitiListener) {
        return new DelegateExpressionTransactionDependentExecutionListener(expressionManager.createExpression(activitiListener.getImplementation()));
    }

    @Override
    public ActivitiEventListener createClassDelegateEventListener(EventListener eventListener) {
        return new DelegateActivitiEventListener(eventListener.getImplementation(), getEntityType(eventListener.getEntityType()));
    }

    @Override
    public ActivitiEventListener createDelegateExpressionEventListener(EventListener eventListener) {
        return new DelegateExpressionActivitiEventListener(expressionManager.createExpression(eventListener.getImplementation()), getEntityType(eventListener.getEntityType()));
    }

    @Override
    public ActivitiEventListener createEventThrowingEventListener(EventListener eventListener) {
        BaseDelegateEventListener result = null;
        if (ImplementationType.IMPLEMENTATION_TYPE_THROW_SIGNAL_EVENT.equals(eventListener.getImplementationType())) {
            result = new SignalThrowingEventListener();
            ((SignalThrowingEventListener) result).setSignalName(eventListener.getImplementation());
            ((SignalThrowingEventListener) result).setProcessInstanceScope(true);
        } else if (ImplementationType.IMPLEMENTATION_TYPE_THROW_GLOBAL_SIGNAL_EVENT.equals(eventListener.getImplementationType())) {
            result = new SignalThrowingEventListener();
            ((SignalThrowingEventListener) result).setSignalName(eventListener.getImplementation());
            ((SignalThrowingEventListener) result).setProcessInstanceScope(false);
        } else if (ImplementationType.IMPLEMENTATION_TYPE_THROW_MESSAGE_EVENT.equals(eventListener.getImplementationType())) {
            result = new MessageThrowingEventListener();
            ((MessageThrowingEventListener) result).setMessageName(eventListener.getImplementation());
        } else if (ImplementationType.IMPLEMENTATION_TYPE_THROW_ERROR_EVENT.equals(eventListener.getImplementationType())) {
            result = new ErrorThrowingEventListener();
            ((ErrorThrowingEventListener) result).setErrorCode(eventListener.getImplementation());
        }

        if (result == null) {
            throw new ActivitiIllegalArgumentException("Cannot create an event-throwing event-listener, unknown implementation type: " + eventListener.getImplementationType());
        }

        result.setEntityClass(getEntityType(eventListener.getEntityType()));
        return result;
    }

    @Override
    public CustomPropertiesResolver createClassDelegateCustomPropertiesResolver(ActivitiListener activitiListener) {
        return classDelegateFactory.create(activitiListener.getCustomPropertiesResolverImplementation(), null);
    }

    @Override
    public CustomPropertiesResolver createExpressionCustomPropertiesResolver(ActivitiListener activitiListener) {
        return new ExpressionCustomPropertiesResolver(expressionManager.createExpression(activitiListener.getCustomPropertiesResolverImplementation()));
    }

    @Override
    public CustomPropertiesResolver createDelegateExpressionCustomPropertiesResolver(ActivitiListener activitiListener) {
        return new DelegateExpressionCustomPropertiesResolver(expressionManager.createExpression(activitiListener.getCustomPropertiesResolverImplementation()));
    }

    /**
     * @param entityType the name of the entity
     * @return
     * @throws ActivitiIllegalArgumentException when the given entity name
     */
    protected Class<?> getEntityType(String entityType) {
        if (entityType != null) {
            Class<?> entityClass = ENTITY_MAPPING.get(entityType.trim());
            if (entityClass == null) {
                throw new ActivitiIllegalArgumentException("Unsupported entity-type for an ActivitiEventListener: " + entityType);
            }
            return entityClass;
        }
        return null;
    }
}
